package xin.lms.aop;

import com.alibaba.fastjson.JSONObject;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import xin.common.utils.AssertUtils;
import xin.common.utils.StringUtils;
import xin.core.AppConstant;
import xin.lms.annotation.Event;
import xin.lms.service.EventService;
import xin.pms.model.User;
import xin.pms.service.UserService;
import xin.core.jwt.JwtTokenService;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * <pre>
 * Opt Log Aspect
 * </pre>
 *
 * @author lixin_ma@outlook.com
 * @version 1.0
 * @since 2018/7/29 13:55
 */
@Slf4j
@Aspect
@Component
public class EventAspect {
    @Autowired
    private EventService service;

    @Autowired
    private UserService userService;

    @Autowired
    private JwtTokenService tokenService;

    @Pointcut("@annotation(xin.lms.annotation.Event)")
    public void pointcut() {
        log.debug(">>>>>pointcut()");

    }

    @After("pointcut()")
    public void doAfter(final JoinPoint joinPoint) {
        log.debug(">>>>>doAfter()");
        try {
            this.logOptLogService(joinPoint, "INFO", "1",Boolean.TRUE);
        } catch (final Exception e) {
            log.error("异常信息:{}", e);
        }
    }

    @AfterThrowing(pointcut = "pointcut()", throwing = "e")
    public void doAfterThrowing(final JoinPoint joinPoint, final Throwable e) {
        log.debug(">>>>>doAfterThrowing()");
        try {
            this.logOptLogService(joinPoint, "ERROR", ExceptionUtils.getStackTrace(e),Boolean.FALSE);
        } catch (final Exception ex) {
            log.error(">>>>>异常信息:{}", ex.getMessage());
        }
    }

    private void logOptLogService(final JoinPoint joinPoint, final String level, final String message,final Boolean success) {
        try {
            final OptLogServiceParameter eventParameter = this.getOptLogServiceParameter(joinPoint, level, message);
            this.logOptLogService(eventParameter,success);
        } catch (final Exception e) {
            log.error("记录系统事件出错 : ", e);
        }
    }

    private void logOptLogService(final OptLogServiceParameter eventParameter,final Boolean success) {
        final HttpServletRequest req =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        final String token  = req.getHeader(AppConstant.JWT_HEADER);
        User currentUser = null;
        if(StringUtils.isNotBlank(token)){
            final String userId = tokenService.getUserId(token.substring(7));
            currentUser = userService.findOne(userId);
        }

        final xin.lms.model.Event event =  xin.lms.model.Event.builder()
                .operatorName(AssertUtils.isNotNull(currentUser) ? currentUser.getUserName() : "guest")
                .operatorId(AssertUtils.isNotNull(currentUser) ? currentUser.getId() : null)
                .operateDate(new Date(System.currentTimeMillis()))
                .detail(JSONObject.toJSONString(eventParameter))
                .name(eventParameter.getName())
                .success(success)
                .build();
        service.save(event);
    }

    protected OptLogServiceParameter getOptLogServiceParameter(final JoinPoint joinPoint, final String level,
                                                               final String message)
            throws Exception {
        final HttpServletRequest req =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        UserAgent userAgent = UserAgent.parseUserAgentString(req.getHeader("User-Agent"));
        final OptLogServiceParameter eventParameter = new OptLogServiceParameter();
        final String targetName = joinPoint.getTarget().getClass().getName();
        final String methodName = joinPoint.getSignature().getName();
        final Object[] arguments = joinPoint.getArgs();
        final Class targetClass = Class.forName(targetName);
        final Method[] methods = targetClass.getMethods();

        eventParameter.setSource(targetName + ":" + methodName);
        eventParameter.setLevel(level);
        eventParameter.setMessage(message);
        eventParameter.setRequest(req.getRequestURI());
        eventParameter.setUserAgent(userAgent);

        for (final Method method : methods) {
            if (method.getName().equals(methodName)) {
                final Class[] methodParameterTypes = method.getParameterTypes();
                if (methodParameterTypes.length == arguments.length) {
                    eventParameter.setName(method.getAnnotation(Event.class).name());
                    eventParameter.setDesc(method.getAnnotation(Event.class).desc());
                    eventParameter.setArguments(StringUtils.join(arguments, ","));
                    break;
                }
            }
        }
        return eventParameter;
    }

    /**
     * 事件参数类
     */
    @Data
    public static class OptLogServiceParameter {
        /**
         * 事件来源
         */
        private String source;
        /**
         * 事件级别
         */
        private String level;
        /**
         * 事件名称
         */
        private String name;
        /**
         * 事件说明
         */
        private String desc;
        /**
         * 事件调用方法参数
         */
        private String arguments;
        /**
         * 事件信息
         */
        private String message;
        /**
         * 事件请求url
         */
        private String request;

        private UserAgent userAgent;
    }
}
